#include "shell.h"
#include "shell_inner.h"

#include "loger.h"




/* static var */

#ifdef CONFIG_SHELL_ENABLE

#define SHELL_CMD_SIZE          CONFIG_SHELL_CMD_SIZE
#define SHELL_CONSOLE_SIZE      CONFIG_SHELL_CONSOLE_SIZE
#define SHELL_TASK_NAME         CONFIG_SHELL_TASK_NAME
#define SHELL_TASK_STACK_SIZE   CONFIG_SHELL_TASK_STACK_SIZE
#define SHELL_TASK_PRIORITY     CONFIG_SHELL_TASK_PRIORITY
#define SHELL_HISTORY_ENABLE    CONFIG_SHELL_HISTORY_ENABLE
#define SHELL_HISTORY_LINES     CONFIG_SHELL_HISTORY_LINES
#define SHELL_AUTH_DEFAULT_PASSWORD CONFIG_SHELL_AUTH_PASSWORD_DEFAULE

static _shell_obj shell_obj;

/* static func */

static const char *shell_get_prompt(void)
{
#define _MSH_PROMPT "sh "
#define _PROMPT     "finsh "
	
    static char shell_prompt[CONFIG_SHELL_CONSOLE_SIZE + 1] = {0};
	static char *finsh_prompt_custom = NULL;

	/* check prompt mode */
	if (!shell_obj.prompt_mode)
	{
		shell_prompt[0] = '\0';
		return shell_prompt;
	}
	
	if(finsh_prompt_custom)
    {
		strncpy(shell_prompt, finsh_prompt_custom, sizeof(shell_prompt)-1);
		return shell_prompt;
    }
	
	if (shell_msh_is_used()) strcpy(shell_prompt, _MSH_PROMPT);
	else strcpy(shell_prompt, _PROMPT);
	
	strcat(shell_prompt, ">");

    return shell_prompt;
}

static void shell_auto_complete(char *prefix)
{
	shell_printf("\n");

	if (shell_msh_is_used() == 1)
	{
		shell_msh_auto_complete(prefix);
	}

	shell_printf("%s%s", shell_get_prompt(), prefix);
}

#ifdef CONFIG_SHELL_HISTORY_ENABLE
static uint8_t shell_handle_history(_shell_obj *shell)
{
	shell_printf("\033[2K\r");
	shell_printf("%s%s", shell_get_prompt(), shell->line);
    return 0;
}

static void shell_push_history(_shell_obj *shell)
{
	if (shell->line_position != 0)
	{
		/* push history */
		if (shell->history_count >= SHELL_HISTORY_LINES)
		{
			/* if current cmd is same as last cmd, don't push */
			if (memcmp(&shell->cmd_history[SHELL_HISTORY_LINES-1], shell->line, SHELL_CMD_SIZE))
			{
				/* move history */
				int index;
				for (index=0; index<SHELL_HISTORY_LINES-1; index++)
				{
					memcpy(&shell->cmd_history[index][0], &shell->cmd_history[index+1][0], SHELL_CMD_SIZE);
				}
				memset(&shell->cmd_history[index][0], 0, SHELL_CMD_SIZE);
                memcpy(&shell->cmd_history[index][0], shell->line, shell->line_position);
				
				/* it's the maximum history */
                shell->history_count = SHELL_HISTORY_LINES;
			}
		}
		else
		{
			 /* if current cmd is same as last cmd, don't push */
            if (shell->history_count == 0 || memcmp(&shell->cmd_history[shell->history_count - 1], shell->line, SHELL_CMD_SIZE))
            {
				shell->current_history = shell->history_count;
				memset(&shell->cmd_history[shell->history_count][0], 0, SHELL_CMD_SIZE);
                memcpy(&shell->cmd_history[shell->history_count][0], shell->line, shell->line_position);
				
				/* increase count and set current history position */
                shell->history_count ++;
			}
		}
	}
	shell->current_history = shell->history_count;
}
#endif


static void shell_task(void *param)
{
    int ch;
    _shell_obj *shell = &shell_obj;

	shell->echo_mode = 1;

	/* set console device as shell device */
	
#ifdef CONFIG_SHELL_AUTH_ENABLE
    /* set the default password when the password isn't setting */
    if (strlen(finsh_get_password()) == 0)
    {
        if (finsh_set_password(SHELL_AUTH_DEFAULT_PASSWORD) != 1)
        {
            shell_printf("Finsh password set failed.\n");
        }
    }
    /* waiting authenticate success */
    finsh_wait_auth();
#endif
	
	shell_printf(shell_get_prompt());
	
	while(1)
	{
		ch = shell_port_get_char();
        if (ch < 0)
        {
            continue;
        }
		
		/*
         * handle control key
         * up key  : 0x1b 0x5b 0x41
         * down key: 0x1b 0x5b 0x42
         * right key:0x1b 0x5b 0x43
         * left key: 0x1b 0x5b 0x44
         */
		if (ch == 0x1b)
        {
            shell->stat = WAIT_SPEC_KEY;
            continue;
        }
		else if (shell->stat == WAIT_SPEC_KEY)
        {
            if (ch == 0x5b)
            {
                shell->stat = WAIT_FUNC_KEY;
                continue;
            }

            shell->stat = WAIT_NORMAL;
        }
		else if (shell->stat == WAIT_FUNC_KEY)
		{
			shell->stat = WAIT_NORMAL;
			
			if (ch == 0x41) /* up key */
            {
			#ifdef CONFIG_SHELL_HISTORY_ENABLE
				/* prev history */
				if (shell->current_history>0)
				{
					shell->current_history--;
				}
				else 
				{
					shell->current_history=0;
					continue;
				}
				
				/* copy the history cmmand */
				memcpy(shell->line, &shell->cmd_history[shell->current_history][0], SHELL_CMD_SIZE);
				shell->line_curpos = shell->line_position = strlen(shell->line);
				shell_handle_history(shell);
			#endif				
				continue;
			}
			else if (ch == 0x42) /* down key */
			{
			#ifdef CONFIG_SHELL_HISTORY_ENABLE
				/* next history */
				if (shell->current_history < (shell->history_count-1))
				{
					shell->current_history++;
				}
				else
				{
					/* set to end of history */
					if(shell->history_count!=0) shell->current_history = shell->history_count-1;
					else continue;
				}
				
				memcpy(shell->line, &shell->cmd_history[shell->current_history][0],SHELL_CMD_SIZE);
				shell->line_curpos = shell->line_position = strlen(shell->line);
				shell_handle_history(shell);
			#endif
				continue;
			}
			else if (ch == 0x44) /* left key */
			{
				if (shell->line_curpos)
				{
					shell_printf("\b");
					shell->line_curpos --;
				}
				continue;
			}
			else if (ch == 0x43) /* right key */
			{
				if (shell->line_curpos < shell->line_position)
				{
					shell_printf("%c", shell->line[shell->line_curpos]);
					shell->line_curpos++;
				}
				continue;
			}
		}
		
		/* received null or error */
        if (ch == '\0' || ch == 0xFF) continue;
		else if (ch == '\t') /* handle tab key */
		{
			int i;
			for (i=0; i<shell->line_curpos; i++)
			{
				shell_printf("\b");
			}
			shell_auto_complete(&shell->line[0]); /* auto complete */
			shell->line_curpos = shell->line_position = strlen(shell->line);
			continue;
		}
		else if (ch == 0x7f || ch == 0x08) /* handle backspace key */
		{
			/* note that shell->line_curpos >= 0 */
			if(shell->line_curpos == 0) continue;
			
			shell->line_curpos--;
			shell->line_position--;
			
			if (shell->line_position > shell->line_curpos)
			{
				int i;
				memmove(&shell->line[shell->line_curpos], &shell->line[shell->line_curpos+1], shell->line_position-shell->line_curpos);
				shell->line[shell->line_position] = '\0';
                shell_printf("\b%s  \b", &shell->line[shell->line_curpos]);
				
				/* move the cursor to the origin position */
				for (i=shell->line_curpos; i<=shell->line_position; i++)
				{
					shell_printf("\b");
				}
			}
			else
			{
				shell_printf("\b \b");
				shell->line[shell->line_position] = 0;
			}
			continue;
		}
		
		if (ch == '\r' || ch == '\n')	/* handle end of line, break */
		{
		#ifdef CONFIG_SHELL_HISTORY_ENABLE
			shell_push_history(shell);
		#endif
			
			if (shell_msh_is_used() == 1)
			{
				if (shell->echo_mode)
				{
					shell_printf("\n");
				}
				shell_msh_exec(shell->line, shell->line_position);
			}
			
			shell_printf(shell_get_prompt());
			memset(shell->line, 0x00, sizeof(shell->line));
			shell->line_curpos = shell->line_position = 0;
			continue;
		}
		
		/* it's a large line, discard it */
		if (shell->line_position >= SHELL_CMD_SIZE)
		{
			shell->line_position=0;
		}
		
		/* normal character */
		if (shell->line_curpos < shell->line_position)
		{
			int i;
			
			memmove(&shell->line[shell->line_curpos+1], &shell->line[shell->line_curpos], shell->line_position-shell->line_curpos);
			shell->line[shell->line_curpos] = ch;
			if (shell->echo_mode)
			{
				shell_printf("%s", &shell->line[shell->line_curpos]);
			}
			
			/* move the cursor to new postion */
			for (i=shell->line_curpos; i<shell->line_position; i++)
			{
				shell_printf("\b");
			}
		}
		else
		{
			shell->line[shell->line_position] = ch;
            if (shell->echo_mode) shell_printf("%c", ch);
		}
		
		ch = 0;
		shell->line_curpos++;
		shell->line_position++;
		if (shell->line_position > SHELL_CMD_SIZE)
		{
			/* clear cmmond line */
			shell->line_position = 0;
			shell->line_curpos = 0;
		}
	}
}



/* export func */

uint8_t shell_get_prompt_mode(void)
{
    return shell_obj.prompt_mode;
}

void shell_set_prompt_mode(uint8_t prompt_mode)
{
    shell_obj.prompt_mode = prompt_mode;
}



int shell_init(void)
{
    // ESP_LOGI(TAG, "shell init");
    // printf("shell init");
    // vTaskDelay(10);
    
    memset(&shell_obj, 0x00, sizeof(_shell_obj));
    shell_msh_init();

    shell_port_init();

	shell_set_prompt_mode(1);
    shell_system_show_version();

    /* shell task create */
    os_task_create( shell_task, 
                    NULL,
                    SHELL_TASK_NAME, 
                    SHELL_TASK_STACK_SIZE, 
                    SHELL_TASK_PRIORITY, 
                    NULL );

    return 0;
}

#else

int shell_init(void)
{
	ESP_LOGI(TAG, "shell not enable.");

	return 0;
}


#endif



